package pkg

import (
	"encoding/json"
	"fmt"
	"gitee.com/wayjin/fabric-extension/cryptoutil"
	"gitee.com/wayjin/fabric-extension/gateway"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"path/filepath"
)

var userCryptoPath = "peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp"

var user1 = "/home/zuvakin/workspace/src/gitee.com/wayjin/fabric-explorer/temp/tom"

type CryptoOption struct {
	HashFamily   string `json:"hashFamily"`
	KeyStorePath string `json:"keystorePath"`
	BasePath     string `json:"basePath"`
}

type peerConfig struct {
	Host        string
	ServiceName string
}

type caSpec struct {
	Host string  `json:"host"`
	TLS  keyPair `json:"tls"`
}

type keyPair struct {
	Cert string `json:"cert"`
	Key  string `json:"key"`
}

type PeerOrgConfig struct {
	MSPID       string `json:"mspid"`
	TLSRootCert string `json:"tlsrootCert"`
	Peers       map[string]peerConfig
	Admin       keyPair `json:"admin"`
	CASpec      caSpec  `json:"caSpec"`
}

func (org PeerOrgConfig) getRootTlsCert() []byte {
	data, err := ioutil.ReadFile(org.TLSRootCert)
	if err != nil {
		panic(err)
	}
	return data
}

type OrdererOrgConfig struct {
	MSPID       string                `json:"mspid"`
	TLSRootCert string                `json:"tlsrootCert"`
	Orderers    map[string]peerConfig `json:"orderers"`
}

func (org OrdererOrgConfig) getRootTlsCert() []byte {
	data, err := ioutil.ReadFile(org.TLSRootCert)
	if err != nil {
		panic(err)
	}
	return data
}

type Config struct {
	CryptoOpt  CryptoOption             `json:"cryptoOpt"`
	PeerOrgs   map[string]PeerOrgConfig `json:"peerOrgs"`
	OrdererOrg OrdererOrgConfig         `json:"ordererOrg"`
}

type Service struct {
	config Config
}

// For test
var userCtx = gateway.UserContext{}

func NewService(configFile string) (*Service, error) {
	data, err := ioutil.ReadFile(configFile)
	if err != nil {
		return nil, err
	}
	var config Config
	err = json.Unmarshal(data, &config)
	if err != nil {
		return nil, err
	}

	//userCtx.CryptoPath = filepath.Join(config.CryptoOpt.BasePath, userCryptoPath)
	userCtx.CryptoPath = user1
	userCtx.MspID = "Org1MSP"

	return &Service{config: config}, nil
}

func (svc Service) getPeerContext(orgName, peerID string) (ctx gateway.PeerContext, err error) {
	orgConfig, ok := svc.config.PeerOrgs[orgName]
	if !ok {
		err = fmt.Errorf("not found config of organization[%s]", orgName)
		return
	}
	peerConfig, ok := orgConfig.Peers[peerID]
	if !ok {
		err = fmt.Errorf("not found config of peer[%s] in organization[%s]", peerID, orgName)
		return
	}

	ctx = gateway.PeerContext{Host: peerConfig.Host, ServiceName: peerConfig.ServiceName,
		RootTLSCert: orgConfig.getRootTlsCert()}
	return
}

func (svc Service) getOrdererContext(ordererID string) (ctx gateway.OrdererContext, err error) {
	ordererConfig, ok := svc.config.OrdererOrg.Orderers[ordererID]
	if !ok {
		err = fmt.Errorf("not found config of orderer [%s]", ordererID)
		return
	}

	ctx = gateway.OrdererContext{
		Host:        ordererConfig.Host,
		ServiceName: ordererConfig.ServiceName,
		RootTLSCert: svc.config.OrdererOrg.getRootTlsCert(),
	}
	return
}

func (svc Service) getDefaultOrderer() (ctx gateway.OrdererContext, err error) {
	for _, config := range svc.config.OrdererOrg.Orderers {
		ctx = gateway.OrdererContext{
			Host:        config.Host,
			ServiceName: config.ServiceName,
			RootTLSCert: svc.config.OrdererOrg.getRootTlsCert(),
		}
		return
	}
	err = fmt.Errorf("not found valid orderer config for service")
	return
}

func (svc Service) getCryptoSuite(mspid, username string, pwd []byte) (cryptoutil.CryptoSuite, error) {
	return cryptoutil.GetMyCryptoSuiteFromPath(filepath.Join(svc.config.CryptoOpt.KeyStorePath, username), mspid, pwd)
}

func (svc Service) getMSPID(orgName string) (string, error) {
	org, ok := svc.config.PeerOrgs[orgName]
	if !ok {
		return "", fmt.Errorf("not found org[%s] config", orgName)
	}
	return org.MSPID, nil
}


// rest api definition
//

// list channel that has been joined on peer
func (svc Service) ListChannels(ctx *gin.Context) {
	listChannel(svc, ctx).Reply(ctx)
}

// get blockchain information
func (svc Service) GetBlockChain(ctx *gin.Context) {
	getBlockChainInfo(svc, ctx).Reply(ctx)
}

// GetBlock: get block from fabric network by number
func (svc Service) GetBlock(ctx *gin.Context) {
	getBlock(svc, ctx).Reply(ctx)
}

// ListBlocks: list top count blocks of hyperledger fabric
func (svc Service) ListBlocks(ctx *gin.Context) {
	listBlocks(svc, ctx).Reply(ctx)
}

// GetChaincode: get chaincode info, not ready
func (svc Service) GetChainCode(ctx *gin.Context) {
	ctx.JSON(200, gin.H{"message": "not ready"})
}

// ListChainCode: list chaincodes of peer that has been installed
func (svc Service) ListChainCode(ctx *gin.Context) {
	listChainCode(svc, ctx).Reply(ctx)
}

// QueryChainCode: chaincode query action
func (svc Service) QueryChainCode(ctx *gin.Context) {
	queryChainCode(svc, ctx).Reply(ctx)
}

// InvokeChainCode: chaincode invoke aciton
func (svc Service) InvokeChainCode(ctx *gin.Context) {
	invokeChainCode(svc, ctx).Reply(ctx)
}

// RegisterUser: user register for query access rest api
func (svc Service) RegisterUser(ctx *gin.Context) {
	enrollUser(svc, ctx)
}
